#ifndef FIELD_H
#define FIELD_H

#include <cmath>

#include <vector>
#include <cstdlib>
#include <string>
#include <iostream>
#include <fstream>

#include "../Tool/Macros.h"
#include "../Tool/Log.h"

struct LocalFields
{
    double x;
    double y;
    double z;
};

struct Vector3d
{
    double x;
    double y;
    double z;
};

//the element type of Field may be: int, double, and Particle.
template<class Type>
class Field
{

public:

    std::string name;

    Field()
    {
    };

    Field( std::vector<int> dims )
    {
    };

    Field( std::vector<int> dims, std::string name_in ) : name(name_in)
    {
    };

    virtual ~Field()
    {
    };

    virtual void allocate_dims(std::vector<int> dims) = 0;


    inline Type& operator () (int i)
    {
        PICDEBUGEXECUTE(if (i>=dims_global_) log_error<<"Out of limits & "<< i;);
        PICDEBUGEXECUTE(if (!std::isfinite(data_[i])) log_error<<"Not finite "<< i << " = " << data_[i];);
        return data_[i];
    };

    inline Type operator () (int i) const
    {
        PICDEBUGEXECUTE(if (i>=dims_global_) log_error<<"Out of limits "<< i;);
        PICDEBUGEXECUTE(if (!std::isfinite(data_[i])) log_error<<"Not finite "<< i << " = " << data_[i];);
        return data_[i];
    };

    inline void set_value(Type val)
    {
        if (data_)
        {
            for (int i=0; i<dims_global_; i++)
            {
                data_[i] = val;
            }
        }
    }

    inline Type& operator () (int i,int j)
    {
        int unsigned idx = i*dims_[1]+j;
        PICDEBUGEXECUTE(if (idx>=dims_global_) log_error<<"Out of limits & "<< i << " " << j;);
        PICDEBUGEXECUTE(if (!std::isfinite(data_[idx])) log_error<<"Not finite "<< i << " " << j << " = " << data_[idx];);
        return data_[idx];
    };

    inline Type operator () (int i, int j) const
    {
        int idx = i*dims_[1]+j;
        PICDEBUGEXECUTE(if (idx>=dims_global_) log_error<<"Out of limits "<< i << " " << j;);
        PICDEBUGEXECUTE(if (!std::isfinite(data_[idx])) log_error<<"Not finite "<< i << " " << j << " = " << data_[idx];);
        return data_[idx];
    };


    inline void copy_from(Field *from_field)
    {
        PICDEBUGEXECUTE(if (dims_global_!=from_field->dims_global_) log_error<<"Field size do not match "<< name << " " << from_field->name;);
        for (int i=0;i< dims_global_; i++)
        {
            (*this)(i)=(*from_field)(i);
        }
    }

    inline std::vector<int> dims () {return dims_;}

    std::vector<int> dims_;

    int dims_global_;

    Type* data_;

protected:

private:

};

#endif
